package com.kunpan.admin.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.github.pagehelper.PageInfo;
import com.kunpan.admin.common.domain.AjaxResult;
import com.kunpan.admin.common.redis.RedisCache;
import com.kunpan.admin.constant.Constants;
import com.kunpan.admin.constant.HttpStatus;
import com.kunpan.admin.constant.RedisMessage;
import com.kunpan.admin.entity.FileShare;
import com.kunpan.admin.entity.PanFile;
import com.kunpan.admin.entity.User;
import com.kunpan.admin.entity.bo.PanFileBo;
import com.kunpan.admin.entity.dto.FileShareDto;
import com.kunpan.admin.entity.vo.FileShareVo;
import com.kunpan.admin.mapper.FileShareMapper;
import com.kunpan.admin.mapper.PanFileMapper;
import com.kunpan.admin.mapper.UserMapper;
import com.kunpan.admin.service.FileShareService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class FileShareServiceImpl implements FileShareService {
    @Autowired
    private FileShareMapper fileShareMapper;

    @Autowired
    private PanFileMapper panFileMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisCache redisCache;

    private static final SecureRandom secureRandom = new SecureRandom();

    /**
     * 获取文件分享列表
     */
    @Override
    public AjaxResult getFileShareList(FileShareDto fileShareDto) {
        Integer userId = Integer.valueOf(StpUtil.getLoginId().toString());
        fileShareDto.setUserId(userId);
        List<FileShareVo> shareVoList = fileShareMapper.selectFileShareList(fileShareDto);
        // 使用 PageInfo 获取分页信息
        PageInfo<FileShareVo> pageInfo = new PageInfo<>(shareVoList);
        Map<String, Object> sharesMap = new HashMap<>();
        sharesMap.put("total", pageInfo.getTotal());
        sharesMap.put("list", pageInfo.getList());
        return AjaxResult.success(sharesMap);
    }

    @Override
    public AjaxResult addFileShare(FileShareDto fileShareDto) {
        FileShare fileShare = new FileShare();
        BeanUtils.copyProperties(fileShareDto, fileShare);
        Integer userId = Integer.valueOf(StpUtil.getLoginId().toString());
        fileShare.setUserId(userId);
        fileShare.setShareStatus(1);
        // 获取当前时间戳（格式化为年月日时分秒）
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        // 生成随机数
        int randomNumber = secureRandom.nextInt(10000);
        // 拼接生成 file_id
        String shareId = timestamp + String.format("%04d", randomNumber);
        fileShare.setShareId(shareId);
        if (fileShareMapper.addFileShare(fileShare) > 0) {
            return AjaxResult.success("创建成功");
        }
        return AjaxResult.error("创建失败,请检查网络连接");
    }

    @Override
    public AjaxResult getFileShareInfo(String pathParam, String code) {
        String key = Constants.CAPTCHA_KEY + pathParam;
        FileShareVo cachedData = redisCache.getCacheObject(key);
        Map<String, Object> map = null;
        if (cachedData != null) {
            map = judgeCode(cachedData, code);
            return AjaxResult.success(map);
        }
//        Integer userId = Integer.valueOf(StpUtil.getLoginId().toString());
        //按照路径查找文件
        cachedData = fileShareMapper.getFileShareInfoByParams(pathParam);
        //文件存在
        if (cachedData != null) {
            //判断文件过期时间有没有到期或者是不是永久有效
            if (cachedData.getExpireTime() == null || cachedData.getExpireTime().after(new Date())) {
                map = judgeCode(cachedData, code == null ? "" : code);
                //设置12小时过期
                redisCache.setCacheObject(key, cachedData, 60 * 60 * 12, TimeUnit.SECONDS);
                return AjaxResult.success(map);
            }
        }
        return AjaxResult.error(HttpStatus.PAGE_NOT_FOUND, "分享不存在");
    }

    @Override
    public AjaxResult updateFileShare(FileShare fileShare) {
        if (fileShareMapper.updateFileShare(fileShare) > 0) {
            String key = Constants.CAPTCHA_KEY + fileShare.getPathParams();
            redisCache.deleteObject(key);
            return AjaxResult.success("修改成功");
        }
        return AjaxResult.error("修改失败");
    }

    /**
     * 根据文件分享id获取文件信息
     */
    @Override
    public AjaxResult getFileShareInfoById(String pathParam, String shareId) {
        String key = Constants.CAPTCHA_KEY + pathParam;
        FileShareVo cachedData = redisCache.getCacheObject(key);
        if (cachedData != null) {
            if (cachedData.getShareId().equals(shareId)) {
                Integer userId = Integer.valueOf(StpUtil.getLoginId().toString());
                cachedData.setSelf(userId.equals(cachedData.getUserId()));
                return AjaxResult.success(cachedData);
            }
            return AjaxResult.error(HttpStatus.PAGE_NOT_FOUND, "分享不存在");
        }
        //按照shareId查找文件
        cachedData = fileShareMapper.getFileShareInfoById(shareId, pathParam);
        //文件存在
        if (cachedData != null) {
            //判断文件过期时间有没有到期或者是不是永久有效
            if (cachedData.getExpireTime() == null || cachedData.getExpireTime().after(new Date())) {
                Integer userId = Integer.valueOf(StpUtil.getLoginId().toString());
                cachedData.setSelf(userId.equals(cachedData.getUserId()));
                //设置12小时过期
                redisCache.setCacheObject(key, cachedData, 60 * 60 * 12, TimeUnit.SECONDS);
                return AjaxResult.success(cachedData);
            }
        }
        return AjaxResult.error(HttpStatus.PAGE_NOT_FOUND, "分享不存在");
    }

    /**
     * 根据id进行分享文件的逻辑删除
     */
    @Override
    public AjaxResult deleteFileShareByIds(String[] ids) {
        fileShareMapper.updateFileShareStatus(ids);
        return AjaxResult.success("操作成功");
    }

    /**
     * 用于判断是否需要提取码码和提取码是否正确
     */
    public Map<String, Object> judgeCode(FileShareVo cachedData, String code) {
        Map<String, Object> map = new HashMap<>();
        if (cachedData.getCode().isEmpty() || cachedData.getCode() == null) {//如果没有设置提取码
            map.put("code", false);//false表示不需要提取码
            map.put("status", "success");
            map.put("fileShareId", cachedData.getShareId());
        } else if (cachedData.getCode().equals(code)) {//如果设置了，而且设置了自动填充提取码
            map.put("code", true);
            map.put("status", "success");
            map.put("fileShareId", cachedData.getShareId());
        } else {//否则设置了提取码，但是提取码不正确
            map.put("code", true);
            map.put("status", "error");
        }
        return map;
    }

    /**
     * 将他人分享的文件添加到自己的网盘
     */
    @Transactional
    @Override
    public AjaxResult addShareFile(FileShareDto fileShareDto) {
        User user = (User) StpUtil.getSession().get(Constants.LOGIN_USER);
        String useSpaceKey = String.format(RedisMessage.USER_SPACE_KEY, user.getUserId().toString());
        Long useSpace = redisCache.getCacheMapValue(useSpaceKey, user.getUserId().toString());
        //如果文件不是文件夹，直接插入即可
        if (fileShareDto.getFileType() > 0) {
            // 1. 查询文件信息
            PanFile panFile = panFileMapper.selectFileInfoById(fileShareDto.getFileId());
            if (panFile.getFileSize() > (user.getTotalSpace() - useSpace)) {
                return AjaxResult.error("添加失败，空间不足");
            }
            panFile.setUserId(user.getUserId());
            panFile.setParentId(fileShareDto.getParentId());
            panFile.setCreateTime(new Date());
            panFile.setUpdateTime(new Date());
            if (panFileMapper.insertFile(panFile) > 0) {
                updateUserSpace(user.getUserId(), panFile.getFileSize(), useSpace, useSpaceKey);
            }
            return AjaxResult.success();
        }
        //文件类型为文件夹,通过文件树快照
        // 2. 生成文件树快照
        panFileMapper.callBuildFileTree(fileShareDto.getFileId(), user.getUserId());
        // 3.查询树形结构所有Ids
        List<Integer> ids = panFileMapper.selectTreeByUserId(user.getUserId());
        // 4.再通过所有的ids查询文件树构完整的数据
        List<PanFileBo> panFileBoList = panFileMapper.selectFileListByIds(ids);
        List<PanFile> insertBatch = new ArrayList<>();
        Long totalSize = 0L;
        // 5.在批量插入文件树结构数据
        for (PanFileBo panFileBo : panFileBoList) {
            totalSize += panFileBo.getFileSize();
            PanFile panFiles = new PanFile();
            BeanUtils.copyProperties(panFileBo, panFiles);
            //将文件夹中的所有非文件夹文件添加到当前文件夹
            panFiles.setParentId(fileShareDto.getParentId());
            panFiles.setUserId(user.getUserId());
            panFiles.setCreateTime(new Date());
            panFiles.setUpdateTime(new Date());
            insertBatch.add(panFiles);
        }
        if (totalSize > (user.getTotalSpace() - useSpace)) {
            return AjaxResult.error("添加失败，空间不足");
        }
        // 6.批量插入
        panFileMapper.batchInsertFiles(insertBatch);
        // 7.清理快照
        panFileMapper.cleanupSnapshot(user.getUserId());
        // 8.更新剩余内存
        updateUserSpace(user.getUserId(), totalSize, useSpace, useSpaceKey);
        redisCache.deleteKeysByPattern(String.format(Constants.FILE_TYPE_LIST_CACHE_KEY_PREFIX, user.getUserId()));
        return AjaxResult.success();
    }

    /**
     * 更新用户内存
     */
    private void updateUserSpace(Integer userId, Long fileSize, Long useSpace, String useSpaceKey) {
        //修改剩余内存
        userMapper.updateUserSpace(userId, fileSize);
        //更新redis中用户剩余空间
        redisCache.setCacheMapValue(useSpaceKey, userId.toString(), useSpace + fileSize);
    }
}
